# src/blockchain.py
import hashlib
import json
import time
import difflib, json
import os
from typing import Optional
from typing import List, Dict, Any
from consensus import verify_authority, poa_seal
from logger import logger

def make_diff(old: str, new: str) -> str:
    """统一 diff 格式"""
    return '\n'.join(
        difflib.unified_diff(
            old.splitlines(keepends=True),
            new.splitlines(keepends=True),
            fromfile='prev', tofile='new'
        )
    )
    
class Block:
    def __init__(self, index: int, timestamp: float,
                 data: Dict[str, Any], prev_hash: str):
        self.index = index
        self.timestamp = timestamp
        self.data = data
        self.prev_hash = prev_hash
        self.hash = self.calculate_hash()

    def calculate_hash(self) -> str:
        block_string = json.dumps({
            "index": self.index,
            "timestamp": self.timestamp,
            "data": self.data,
            "prev_hash": self.prev_hash
        }, sort_keys=True)
        return hashlib.sha256(block_string.encode()).hexdigest()

    def __repr__(self):
        return f"Block#{self.index} {self.hash[:8]}... @{int(self.timestamp)}"
    


    def is_valid(self) -> bool:
        for i in range(1, len(self.chain)):
            curr = self.chain[i]
            prev = self.chain[i-1]
            if curr.prev_hash != prev.hash:
                return False
            if curr.hash != curr.calculate_hash():
                return False
        return True

    def to_list(self) -> List[Dict]:
        return [b.__dict__ for b in self.chain]
    
  
    
    def forge_block_poa(self, node_id: str, private_key: str):
        """PoA 方式生成新区块"""
        if not verify_authority(node_id):
            raise PermissionError("Not an authority node")
        last_block = self.last_block
        proof = poa_seal(last_block.hash, private_key)
        return self.new_block(proof, last_block.hash)
    
class Blockchain:
    def __init__(self):
        self.chain = [self._genesis_block()]

    def _genesis_block(self) -> "Block":
        return Block(index=0, timestamp=time.time(), data={"genesis": True}, prev_hash="0")

    @property
    def last_block(self) -> "Block":
        return self.chain[-1]

    def new_block(self, proof: int, prev_hash: str) -> "Block":
        # 占位，可根据 PoW 需要扩展
        return Block(
            index=len(self.chain),
            timestamp=time.time(),
            data={"proof": proof},
            prev_hash=prev_hash,
        )
    def add_block(self, new_config: str, **meta) -> "Block":
        prev_block = self.last_block
        prev_config = prev_block.data.get("config", "")
        diff = make_diff(prev_config, new_config)
        data = {
            "device_id": meta.get("device_id", "unknown"),
            "config": new_config,
            "diff": diff,
            **meta
        }
        block = Block(
            index=len(self.chain),
            timestamp=time.time(),
            data=data,
            prev_hash=prev_block.hash
        )
        self.chain.append(block)
        logger.info("new block forged", index=block.index, hash=block.hash)
        return block
    def is_valid(self) -> bool:
        for i in range(1, len(self.chain)):
            curr = self.chain[i]
            prev = self.chain[i-1]
            if curr.prev_hash != prev.hash:
                return False
            if curr.hash != curr.calculate_hash():
                return False
        return True
    def to_list(self) -> list:
        return [b.__dict__ for b in self.chain]